package com.cody.puzzletest;

import android.content.Context;
import android.content.SharedPreferences;
import android.view.View;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.view.MotionEvent;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import android.os.Handler;
import android.util.Log;




public class PuzzleTestView extends View implements OnGestureListener {

	private Gem[][] gemGrid; //Holds the Gems
	private Gem[][] nextGems; //Holds the next two lines of gems
	private boolean[][] fallingGrid;
	private boolean toResetFallings; //Whether the gems must be reset for their falling targets
	private boolean resetFirst; //Reset fallings at the very start of a gem being switched
	private SelectedGem sGem; //Information about the selected Gem
	private GestureDetector gDetector; //Detects gestures
	private Set<Gem> fallingGems; //Holds the gems that are falling
	private List<Set<Gem>> pausedGems; //Holds the gems that are paused and going to be added to the fallings
	private List<Set<Gem>> matchedGems; //LinkedList to hold the ArrayLists of matched Gems
	//Would be easy to implement scoring, just see the size of the ArrayList you use. Simple.
	private Handler mHandler = new Handler(); //Handler for timing of gems and such.
	private boolean gemsFalling; //whether gems are falling
	private Map<Long, Gem> currentMatches; //The ordered gems (in timed order)
	private List<Set<Gem>> toRemove; //Gems to remove next from the grid. 
	private int offset; //Difference between normal row position and current position
	private boolean running; //whether the game is scrolling upwards
	private int moving_speed; //how fast the game is moving upwards
	private boolean gameOver;
	private boolean fromMatches; //tells the fallings whether they are from a match or not
	
	public final Bitmap TOTAL_BORDER = 
			BitmapFactory.decodeResource(this.getResources(), R.drawable.section_border);
	public final Bitmap SELECTED_BORDER = 
			BitmapFactory.decodeResource(this.getResources(), R.drawable.border);
	
	private Context con;
	private Paint p;
	
	private int speed;
	private int all_time_high;
	private int current_high;
	private SharedPreferences scores;
	
	public final int START_ROWS = 6; //Number of rows in beginning
	public final int START_COLUMNS = 6; //Number of columns in beginning
	public final int TOTAL_ROWS = 10; //Total possible rows
	public final int TOTAL_COLUMNS = 6; //Total possible columns
	public final int GEM_SIZE = 60; //Pixel size of gems
	public final int SPACE_BTWN = 10; //Space between gems
	public final int START_X = 20; //Starting X location of 0,0
	public final int START_Y = 700; //Starting X location of 0,0
	public final int GEM_TOTAL = 60; //GEM_SIZE + SPACE
	public final int BORDER_WIDTH = 10; //Width of "selected" border
	public final int FALLING_SPEED = 1;
	public final int FALLING_DISTANCE = 15;
	public final int DISAPPEAR_SPEED = 175; //175
	public final int MATCHED_PAUSE = 200;
	public final int FALLING_PAUSE = 200;
	public final int OFFSET_CHANGE = 4;
	public final int STARTING_SPEED = 400;
	
	PuzzleTestView(Context context) {
		super(context);
		speed = 1;
		current_high = 0;
	    scores = context.getSharedPreferences("defaultFile", 0);
		all_time_high = scores.getInt("high", 0);
		con = context;
		running = true;
		fromMatches = false;
		gameOver = false;
		pausedGems = new ArrayList<Set<Gem>>();
		toRemove = new ArrayList<Set<Gem>>();
		currentMatches = new TreeMap<Long, Gem>();
		fallingGems = new TreeSet<Gem>();
		matchedGems = new LinkedList<Set<Gem>>();
		gDetector = new GestureDetector(this);
		fallingGrid = new boolean[TOTAL_ROWS][TOTAL_COLUMNS];
		toResetFallings = false;
		resetFirst = false;
		gemGrid = new Gem[TOTAL_ROWS][TOTAL_COLUMNS];
		nextGems = new Gem[2][TOTAL_COLUMNS];
		sGem = new SelectedGem();
		gemsFalling = false;
		loadGrid();
		offset = 0;;
		moving_speed = STARTING_SPEED;
		p = new Paint();
		Typeface t = Typeface.create(Typeface.SERIF, Typeface.BOLD);
		p.setTypeface(t);
		p.setAntiAlias(true);
		p.setTextSize(30);
		p.setColor(Color.WHITE);
		p.setTextAlign(Paint.Align.LEFT);
	}
	
	//Constantly runs and updates the stack of gems upwards
	private Runnable updateOffset = new Runnable() {
		public void run() {
			System.out.println("updateOffset");
			if(running && !gemsFalling && toRemove.isEmpty() && matchedGems.isEmpty()) {
				offset += OFFSET_CHANGE;
				if(offset == 60 - OFFSET_CHANGE) {
					if(!topRowIsEmpty()) {
						running = false;
						mHandler.removeCallbacks(updateOffset);
						mHandler.postDelayed(isGameOver, 2000);
					}
				} else if(offset == 60) {
					transferRows();
				}
			}
			if(running) {
				mHandler.removeCallbacks(updateOffset);
				mHandler.postDelayed(updateOffset, moving_speed);
			}
			invalidate();
		}
	};
	
	private void fallingsTransferUpdate() {
		System.out.println("fallingsTupdate");
		Iterator<Gem> iterator = fallingGems.iterator();
		while(iterator.hasNext()) {
			Gem g = iterator.next();
			if(g.oldRow < TOTAL_ROWS - 1) {
				g.oldRow++;
			}
			g.row++;
		}
		
		for(int i = 0; i < toRemove.size(); i++) {
			iterator = toRemove.get(i).iterator();
			while(iterator.hasNext()) {
				Gem g = iterator.next();
				if(g.oldRow < TOTAL_ROWS - 1) {
					g.oldRow++;
				}
				g.row++;
			}
		}
		for(int i = 0; i < pausedGems.size(); i++) {
			iterator = pausedGems.get(i).iterator();
			while(iterator.hasNext()) {
				Gem g = iterator.next();
				g.oldRow++;
			}
		}
	}
	
	private void transferRows() {
		System.out.println("transRows");
		boolean falls = false;
		if (!fallingGems.isEmpty())
			falls = true;
		for(int i = TOTAL_ROWS - 1; i > 0; i--) {
			gemGrid[i] = gemGrid[i-1];
			if(falls) {
				fallingGrid[i] = fallingGrid[i-1];
			}
		}
		if(sGem.row < TOTAL_ROWS - 1) {
			sGem.row++;
		}
		gemGrid[0] = nextGems[1];
		nextGems[1] = nextGems[0];
		nextGems[0] = getNextRow();
		offset = 0;
		if(!currentMatches.isEmpty()) {
			Set<Long> keys = currentMatches.keySet();
			for(long l: keys) {
				currentMatches.get(l).row++;
			}
		}
		if(!toRemove.isEmpty()) {
			for(int i = 0; i < toRemove.size(); i++) {
				Iterator<Gem> iterator = toRemove.get(i).iterator();
				while(iterator.hasNext()) {
					iterator.next().row++;
				}
			}
		}
		//fallingsTransferUpdate();
		checkMatches();
	}
	
	private Runnable isGameOver = new Runnable() {
		public void run() {
			System.out.println("isGaO");
			
			if(!running) {
				if(!matchedGems.isEmpty() || !toRemove.isEmpty()) {
					mHandler.removeCallbacks(isGameOver);
					mHandler.postDelayed(isGameOver, 50);
				} else {
					if(!topRowIsEmpty()) {
					gameOver = true;
					if(current_high == all_time_high) {
						SharedPreferences.Editor editor = scores.edit();
						editor.putInt("high", current_high);
						editor.commit();
					}
					invalidate();
					} else {
						running = true;
						mHandler.removeCallbacks(updateOffset);
						mHandler.postDelayed(updateOffset, moving_speed);
					}
				}
			}
		}
	};
	
	//Removal of gems is as follows: A.) Place set of gems in a group. Then gems are added to the "currentGems"
	//Based on time of removal. Then, gems are removed from the grid and fallings are reset.
	private Runnable matchCleanup = new Runnable() {
		public void run() {
			System.out.println("mclean");
			Set<Gem> list = toRemove.remove(0);
			Iterator<Gem> i = list.iterator();
			while(i.hasNext()) {
				Gem g = i.next();
				int row = g.row;
				int column = g.column;
				gemGrid[row][column] = null;
			}
			current_high += list.size() * 5 * speed;
			int target =  ((speed) * (speed + 1) / 2) * 125;
			if(current_high > target) {
				speed++;
				moving_speed = STARTING_SPEED - (25 * speed);
			}
			if(current_high > all_time_high) {
				all_time_high = current_high;
			}
			fromMatches = true;
			resetFirst = true;
			setFallings();
			if(matchedGems.isEmpty() && toRemove.isEmpty()) {
				mHandler.removeCallbacks(updateOffset);
				mHandler.postDelayed(updateOffset, 500);
				if(!running && topRowIsEmpty()) {
					running = true;
				}
			}
			invalidate();
		}
	};
	
	//Removes visible matches
	private Runnable removeMatchesHandler = new Runnable() {
		public void run() {
			System.out.println("remomatchhand");

			Set<Gem> matches =  ((LinkedList<Set<Gem>>) matchedGems).removeFirst();
			toRemove.add(matches);
			mHandler.postDelayed(matchCleanup, DISAPPEAR_SPEED * (matches.size() - 1) + 5);
			int i = 0;
			for(Gem g: matches) {
				long t = System.currentTimeMillis() + DISAPPEAR_SPEED * i;
				currentMatches.put(t, g);
				mHandler.postDelayed(removeGem, DISAPPEAR_SPEED *i);
				i++;
			}
		}
	};
	
	//Removes the next gem
	private Runnable removeGem = new Runnable() {
		public void run() {
			System.out.println("remogem");
			Gem g = currentMatches.remove(((TreeMap<Long, Gem>) currentMatches).firstKey());
			g.disappeared = true;
			invalidate();
		}
	};
	
	
	private Runnable adjustPause = new Runnable() {
		public void run() {
			Set<Gem> s = pausedGems.remove(0);
			Iterator<Gem> i = s.iterator();
			while(i.hasNext()) {
				Gem g = i.next();
				fallingGems.add(g);
				gemGrid[g.oldRow][g.column] = null;
			}
			mHandler.removeCallbacks(adjustFalling);
			resetFirst = true;
			mHandler.post(adjustFalling);
		}
	};
	
	//Makes gems fall at falling_speed
	private Runnable adjustFalling = new Runnable() {
		public void run() {
			System.out.println("adjustFalling");
			boolean[][] fallingHelps = new boolean[TOTAL_ROWS][TOTAL_COLUMNS];
			boolean gemFinished = false;
			if(!gemsFalling || resetFirst) {
				gemsFalling = true;
				resetFirst = false;
				setTargetsHelper();
			}
			Iterator<Gem> iterator = ((TreeSet<Gem>) fallingGems).iterator();
			while(iterator.hasNext()) {
				Gem g = iterator.next();
				int row1 = (int) ((START_Y - offset + GEM_TOTAL - 1 - g.currY) / GEM_TOTAL);
				int row2 = (int) ((START_Y - offset - g.currY) / GEM_TOTAL);
				int column = g.column;
				if(row1 >= 0 && row1 < TOTAL_ROWS)
					fallingHelps[row1][column] = true;
				if(row2 >= 0 && row2 < TOTAL_ROWS) {
					fallingHelps[row2][column] = true;
				}
				g.currY = g.currY + FALLING_DISTANCE;
				if(g.currY >= getRowCoords(g.row)) {
					gemGrid[g.row][g.column] = g;
					g.falling = false;
					iterator.remove();
					gemFinished = true;
					if(toResetFallings) {
						setTargetsHelper();
					}
				}
			}
			if(gemFinished) {
				checkMatches();
				 if(!running && topRowIsEmpty() && toRemove.isEmpty() && matchedGems.isEmpty()) {
						running = true;
						mHandler.removeCallbacks(updateOffset);
						mHandler.postDelayed(updateOffset, moving_speed);
				   }
			}
			invalidate();
			if(!fallingGems.isEmpty()) {
				fallingGrid = fallingHelps;
				mHandler.removeCallbacks(adjustFalling);
				mHandler.postDelayed(adjustFalling, FALLING_SPEED);
			} else {
				gemsFalling = false;
				toResetFallings = false;
				fallingGrid = new boolean[TOTAL_ROWS][TOTAL_COLUMNS];
			}
		}
	};
	
	//Sets the target for falling gems
	private void setFallTargets(int column, int countSpaces, int row, Gem current, Iterator<Gem> list) {
		System.out.println("setFalTargs");
		if(current.column == column && current.oldRow >= row) {
			if(current.oldRow == row) {
				current.row = current.oldRow - countSpaces;
				if(list.hasNext()) {
					current = list.next();
					setFallTargets(column, countSpaces, row + 1, current, list);
				}
			} else {
				while(row < current.oldRow) {
					if(gemGrid[row][column] == null) {
						countSpaces++;
					}
					row++;
				}
				setFallTargets(column, countSpaces, row, current, list);
			}
		} else {
			setFallTargets(current.column, 0, 0, current, list);
		}
	}
	
	//Returns whether the top row is empty or not
	private boolean topRowIsEmpty() {
		System.out.println("topRowEmpty");

		for(int i = 0; i < TOTAL_COLUMNS; i++) {
			if(gemGrid[TOTAL_ROWS - 1][i] != null && !gemGrid[TOTAL_ROWS - 1][i].falling) {
				return false;
			}
		}
		return true;
	}
	
	//Calls the appropriate touch event
	@Override
    public boolean onTouchEvent(MotionEvent me) {
        return gDetector.onTouchEvent(me);
    }
   
	//Determines whether selection is legal 
   private boolean isLegalSelection(int row, int column) {
	   System.out.println("legalSelection");
	   if (column >= TOTAL_COLUMNS || row >= TOTAL_ROWS || row == -1 || column == -1 
			   		|| gemGrid[row][column] == null
			   		|| fallingGrid[row][column]
					|| gemGrid[row][column].selected
					|| gemGrid[row][column].falling) {
		   sGem.selected = false;
		   return false;
	   } else {
		   sGem.row = row;
		   sGem.column = column;
		   return true;
	   }
   }
   
   
   private void setTargetsHelper() {
	   Iterator<Gem> iteratorOne = ((TreeSet<Gem>) fallingGems).iterator();
	   if(iteratorOne.hasNext()) {
		   Gem g = iteratorOne.next();
		   setFallTargets(g.column, 0, 0, g, iteratorOne);
	   }
   }

   private void checkMatches() {
		System.out.println("checkMatches");

	   List<Gem> matchesList = new ArrayList<Gem>();
	   //Check each row for matches
	   for(int i = 0; i < TOTAL_ROWS; i++) {
		   int count = 1;
		   int col = 0;
		   while(col < TOTAL_COLUMNS && (gemGrid[i][col] == null 
				   || gemGrid[i][col].falling || gemGrid[i][col].selected)) {
					   
			   //If a gem isn't in position to be matched, don't check it
			   col++;
		   }
		   if(col < TOTAL_COLUMNS) {
			   int type = gemGrid[i][col].GEM_TYPE;
			   for(int j = col + 1; j < TOTAL_COLUMNS; j++) {
				   if(gemGrid[i][j] == null || gemGrid[i][j].selected || gemGrid[i][j].falling) {
					   checkRowHelper(i, j, count, matchesList);
					   count = 0;
				   } else if(count == 0) {
					   type = gemGrid[i][j].GEM_TYPE;
					   count = 1;
				   } else {
					   if(type == gemGrid[i][j].GEM_TYPE) {
						   count++;
					   } else {
						   checkRowHelper(i, j, count, matchesList);
						   count = 1;
						   type = gemGrid[i][j].GEM_TYPE;
					   }
				   }
			   }
			   checkRowHelper(i, TOTAL_COLUMNS, count, matchesList);
		   }
	   }
	   for(int i = 0; i < TOTAL_COLUMNS; i++) {
		   int count = 1;
		   int type = -1;
		   if(gemGrid[0][i] != null && !gemGrid[0][i].falling) {
			   if(!gemGrid[0][i].selected) {
				   type = gemGrid[0][i].GEM_TYPE;
			   }
			   for(int j = 1; j < TOTAL_ROWS; j++) {
				   //If the gem is null, move to the next column (as falling/nothing is above)
				   if(gemGrid[j][i] == null || gemGrid[j][i].falling) {
					   checkColumnHelper(i, j, count, matchesList);
					   count = 0;
					   break;
				   //If the gem is selected, set the count to 0, check below
				   } else if(gemGrid[j][i].selected) {
					   checkColumnHelper(i, j, count, matchesList);
					   count = 0;
				   } else if(count == 0) {
					   type = gemGrid[j][i].GEM_TYPE;
					   count = 1;
				   } else {
					   if(type == gemGrid[j][i].GEM_TYPE) {
						   count++;
					   } else {
						   checkColumnHelper(i, j, count, matchesList);
						   count = 1;
						   type = gemGrid[j][i].GEM_TYPE;
					   }
				   }
			   }
			   checkColumnHelper(i, TOTAL_ROWS, count, matchesList);
		   }
	   }
	   Set<Gem> mList = new TreeSet<Gem>(matchesList);
	   Iterator<Gem> i = mList.iterator();
	   while(i.hasNext())
		   i.next().selected = true;
	   invalidate();
	   if(!matchesList.isEmpty()) {
		   matchedGems.add(mList);
		   mHandler.postDelayed(removeMatchesHandler, MATCHED_PAUSE);
        }
   }
   
   //Determines the gems that are falling
   private void setFallings() {
		System.out.println("setFalls");
		Set<Gem> l = new TreeSet<Gem>();
	    boolean newGems = false;
	    for(int i = 0; i < TOTAL_COLUMNS; i++) {
		   boolean falling = false;
		   for(int j = 0; j < TOTAL_ROWS; j++) {
			   if(gemGrid[j][i] == null || gemGrid[j][i].falling) {
				   falling = true;
			   } else if(gemGrid[j][i].selected) {
				   falling = false;
			   } else if(falling) {
				   newGems = true;
				   Gem g = gemGrid[j][i];
				   g.falling = true;
				   g.column = i;
				   g.oldRow = j;
				   g.currY = getRowCoords(j); 
				   if(!fromMatches && canPause(g.oldRow, g.column)) {
					   l.add(g);
				   } else {
					   fallingGems.add(g);
					   gemGrid[g.oldRow][g.column] = null;
				   }
			   }
		   }
	    }
	    if(!l.isEmpty()) {
	    	gemsFalling = true;
	    	pausedGems.add(l);
	    	mHandler.postDelayed(adjustPause, FALLING_PAUSE);
	    }
	    if(!fallingGems.isEmpty()) {
		   if(!gemsFalling) {
			   gemsFalling = true;
			   resetFirst = true;
			   mHandler.removeCallbacks(adjustFalling);
			   mHandler.post(adjustFalling);
		   } else if(newGems) {
			   resetFirst = true;
		   }
	   }
	   fromMatches = false;
   }
   
   
   private boolean canPause(int row, int column) {
	   for(int i = row + 1; i < TOTAL_ROWS; i++) {
		   if(fallingGrid[i][column] 
				   || (gemGrid[i][column] != null && gemGrid[i][column].falling)) {
			   return false;
		   }
	   }
	   return true;
   }
   
   //Adds matching gems to List
   private void checkRowHelper(int i, int j, int count, List<Gem> matches) {
		//System.out.println("checkRowHelp");

	   if(count > 2) {
		   while(count > 0) {
			   Gem gem = gemGrid[i][j - count];
			   gem.row = i;
			   gem.column = j - count;
			   matches.add(gem);
			   count--;
			   mHandler.removeCallbacks(updateOffset);
		   }
	   }
   }
   
   //Adds matching gems to List
   private void checkColumnHelper(int i, int j, int count, List<Gem> matches) {
		//System.out.println("checkColHelp");

	   if(count > 2) {
		   while(count > 0) {
			   if(!matches.contains(gemGrid[j - count][i])) {
				   Gem gem = gemGrid[j - count][i];
				   gem.row = j - count;
				   gem.column = i;
				   matches.add(gem);
				   mHandler.removeCallbacks(updateOffset);
			   }
			   count--;
		   }
	   }
   }
   
   //Returns coordinates of a given row
   private int getRowCoords(int row) {
	   return START_Y - offset - row * GEM_TOTAL;
   }
   
   //Determines column of the given location (in motionevent), returns -1 for illegal selection
   private int getColumn(MotionEvent e) {
		System.out.println("getCol");

	   float xpos = e.getX();
	   int column = (int) ((xpos - START_X) / GEM_TOTAL);
	   if(column < 0) {
		   return -1;
	   } else if(column >= TOTAL_COLUMNS) {
		   return TOTAL_COLUMNS;
	   } else {
		   return column;
	   }
   }
   
   
   //Because the Y-axis begins at 0 and works upwards rather than down,and java rounds ints down,
   //659 (Start_y + gemTotal - 1) must be used instead of 600 to accurately calculate row
   private int getRow(MotionEvent e) {
	   System.out.println("getRow");
	   float ypos = e.getY();
	   int row = (int) ((START_Y - offset + GEM_TOTAL - 1 - ypos) / GEM_TOTAL);
	   if(row < 0 || row >= TOTAL_ROWS) {
		   return -1;
	   } else {
		   return row;
	   }
   }
   
   @Override
   public void onShowPress(MotionEvent e) {
   }    

 //On a downclick, determines whether to select, switch, or deselect gems
   @Override
   public boolean onDown(MotionEvent e) {
		System.out.println("onDown");
		System.out.println("" + sGem.row + ", " + sGem.column);
	   int column = getColumn(e);
	   int row = getRow(e);
		   //If the click is in an invalid area, deselect
	   if (column >= TOTAL_COLUMNS || row >= TOTAL_ROWS || row == -1 || column == -1 
			    || fallingGrid[row][column]
		   		|| (gemGrid[row][column] != null
				&& gemGrid[row][column].selected)) {
		   if(sGem.selected) {
			   	sGem.selected = false;
			   	invalidate();
		   }
		//If a gem is selected, see if a switch is possible, and switch. Then check matches. 
	   } else if(sGem.selected) {
		   Gem first = gemGrid[row][column];
		   Gem second = gemGrid[sGem.row][sGem.column];
		   //Must be next to each other, and not the same type in order to switch
		   if(Math.abs(column - sGem.column) == 1 && sGem.row == row
				   								  && !second.equalsType(first)) {
			  moveGems(row, column, first, second);
		   }
	       sGem.selected = false;
	       invalidate();
	   }
	   //None are selected, and it is a legal selection.
	   else {
		   sGem.row = row;
	   	   sGem.column = column;
	   	   sGem.selected = true;
	   	   System.out.println("TRUE! , " + sGem.row + "," + sGem.column);
	   	   invalidate();
	   }
       return true;
   }
   
   
   //Switches gems
   private void moveGems(int row, int column, Gem first, Gem second) {
	   System.out.println("moveGems");
	   if(!fallingGrid[sGem.row][sGem.column] && !fallingGrid[row][column] &&
			   !((first != null && first.falling) || second != null && second.falling)) {
		   //Tests if first or second is null. Also makes sure that the one above is not falling, so you can't
		   //sneak a gem under a paused gem.
		   if((first == null && !(sGem.row + 1 < TOTAL_ROWS && gemGrid[sGem.row + 1][column] != null && gemGrid[sGem.row + 1][column].falling))
				   || (second == null && !(row + 1 < TOTAL_ROWS && gemGrid[row + 1][column] != null && gemGrid[row + 1][column].falling))) {
			   switchGemHelper(row, column, first, second);
			   if(gemsFalling || !fallingGems.isEmpty()) {
				   if((first != null && first.falling) || (second != null && second.falling)) {
					   toResetFallings = true;
					   resetFirst = true;
				   }
			   }
			   setTargetsHelper();
		   } else if(first.GEM_TYPE != second.GEM_TYPE)
			   switchGemHelper(row, column, first, second);
		   sGem.selected = false;
	   }
   }
   
   private void switchGemHelper(int row, int column, Gem first, Gem second) {
	   System.out.println("" + sGem.row + "," + sGem.column);
	   gemGrid[row][column] = second;
	   gemGrid[sGem.row][sGem.column] = first;
	   setFallings();
	   checkMatches();
	   sGem.selected = false;
   }
   
   @Override
   public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
	   System.out.println("onFling");
		System.out.println("sGEM" + sGem.row + ", " + sGem.column);
	   int columnA = getColumn(e1);
	   int rowA = getRow(e1);
	   System.out.println("" + columnA + ", " + rowA);
	   if(isLegalSelection(rowA, columnA)) {
		   int columnB = getColumn(e2);
		   int rowB = getRow(e2);
		   System.out.println("" + rowB + "," + columnB);
			   if(rowA == rowB) {
				   Gem second = gemGrid[rowA][columnA];
				   if(columnB < columnA && columnA > 0) {
					   Gem first = gemGrid[rowA][columnA - 1];
					   if(first == null || !first.selected) {
						   moveGems(rowB, columnA - 1, first, second);
					   }
				   } else if (columnB > columnA && columnA + 1 < TOTAL_COLUMNS) {
					   Gem first = gemGrid[rowA][columnA + 1];
					   if(first == null || !first.selected) {
						   moveGems(rowB, columnA + 1, first, second);
					   }
				   }
			   }
		   invalidate();
		   return true;
	   }
	   invalidate();
       return false;
   }
   
   @Override
   public void onLongPress(MotionEvent e) {
	   current_high = 0;
	   speed = 1;
	   offset = 0;
	   gameOver = false;
	   mHandler.removeCallbacks(isGameOver);
	   mHandler.removeCallbacks(updateOffset);
	   mHandler.postDelayed(updateOffset, moving_speed);
	   mHandler.removeCallbacks(adjustFalling);
	   mHandler.removeCallbacks(removeMatchesHandler);
	   mHandler.removeCallbacks(adjustPause);
	   mHandler.removeCallbacks(removeGem);
	   mHandler.removeCallbacks(matchCleanup);
	   gemGrid = new Gem[TOTAL_ROWS][TOTAL_COLUMNS];
	   loadGrid();
	   sGem.selected = false;
	   fallingGems = new TreeSet<Gem>();
	   matchedGems = new LinkedList<Set<Gem>>();
	   sGem.selected = false;
	   gemsFalling = false;
	   toRemove = new ArrayList<Set<Gem>>();
	   currentMatches = new TreeMap<Long, Gem>();
	   running = true;
	   fallingGrid = new boolean[TOTAL_ROWS][TOTAL_COLUMNS];
   }
   
   //Guarantees distinguished moves
   @Override
   public boolean onSingleTapUp(MotionEvent e) {
       return true;
   }
   
   @Override
   public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
       return true;
   }
   
   
   //Draws the Screen again
   @Override
   protected void onDraw(Canvas c) {
		System.out.println("onDraw");

	   super.onDraw(c);
	   if(gemGrid[sGem.row][sGem.column] == null) {
		   sGem.selected = false;
	   }
	   c.drawBitmap(TOTAL_BORDER, START_X - 3, START_Y - 3 - GEM_TOTAL * TOTAL_ROWS + (OFFSET_CHANGE - 2), null);
	   if(gemGrid != null) {
		   //Draws each gem
		   for(int i = 0; i < TOTAL_ROWS; i++) {
			   int ypos = getRowCoords(i);
			   for(int j = 0; j < TOTAL_COLUMNS; j++) {
				   if(gemGrid[i][j] != null && !gemGrid[i][j].falling && !gemGrid[i][j].disappeared) {
					   int xpos = j * GEM_TOTAL + START_X;
					   c.drawBitmap(gemGrid[i][j].getGem(), xpos, ypos , null);
					   Log.w("" + gemGrid[i][j].selected, "");
					   if(gemGrid[i][j].selected) {
						   c.drawBitmap(gemGrid[i][j].getBorder(), 
								   xpos - BORDER_WIDTH/2, ypos - BORDER_WIDTH/2 , null);
					   }
				   }
			   }
			}
		}
	    for(Gem g: fallingGems) {
	    	int xpos = g.column * GEM_TOTAL + START_X;
	    	int ypos = g.currY;
	    	c.drawBitmap(g.getGem(), xpos, ypos, null);
	    }
	    for(int i = 0; i < pausedGems.size(); i++) {
	    	for(Gem g: pausedGems.get(i)) {
	    		int xpos = g.column * GEM_TOTAL + START_X;
		    	int ypos = g.currY;
		    	c.drawBitmap(g.getGem(), xpos, ypos, null);
	    	}
	    }
	    //Draws the border for the "selected" gem
		if(sGem.selected) {
			int xpos = sGem.column * GEM_TOTAL + START_X - BORDER_WIDTH/2;
			int ypos = START_Y - 5 - sGem.row * GEM_TOTAL - offset;
			c.drawBitmap(SELECTED_BORDER, xpos, ypos, null);
		}
		for(int i = 0; i < 2; i++) {
			for(int j = 0; j < TOTAL_COLUMNS; j++) {
				Gem g = nextGems[i][j];
				int xpos = j * GEM_TOTAL + START_X;
				int ypos = START_Y + 120 - (GEM_TOTAL * i) - offset;
				c.drawBitmap(g.getDead(), xpos, ypos, null);
			}
		}
		if(gameOver) {
			c.drawBitmap(BitmapFactory.decodeResource(this.getResources(), 
					R.drawable.game_over), 0, 200, null);
		}
		c.drawText("SCORE: " + current_high, 10, 60, p);
		c.drawText("SPEED: " + speed + "x", 275, 60, p);
	    c.rotate(90);
	    c.drawText("HIGH SCORE: " + all_time_high, 100, -400, p);
	}
	
	//Beginning grid loading
	private void loadGrid() {
		for(int i = 0; i < START_ROWS; i++) {
			for(int j = 0; j < START_COLUMNS; j++) {
				int next = Gem.checkPlacement(i, j, gemGrid);
				gemGrid[i][j] = new Gem(next, getContext());
			}
		}
		nextGems[0] = getNextRow();
		nextGems[1] = getNextRow();
		mHandler.postDelayed(updateOffset, moving_speed);
		invalidate();
	}
	
	private Gem[] getNextRow() {
		Gem[] result = new Gem[TOTAL_COLUMNS];
		for(int i = 0; i < TOTAL_COLUMNS; i++) {
			int next = Gem.checkDeadPlacement(i, result);
			result[i] = new Gem(next, getContext());
		}
		return result;
	}
	
	
	
	//Class to maintain information about the selected Gem
	public class SelectedGem {
		public boolean selected;
		public int row;
		public int column;
		
		public SelectedGem() {
			selected = false;
			this.row = 0;
			this.column = 0;
		}
	}
}